/* Copyright (C) 2001-2019 Peter Selinger.
   This file is part of Potrace. It is free software and it is covered
   by the GNU General Public License. See the file COPYING for details. */


/* The PGM backend of Potrace. Here we custom-render a set of Bezier
   curves and output the result as a greymap. This is merely a
   convenience, as the same could be achieved by piping the EPS output
   through ghostscript. */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <math.h>

#include "backend_pgm.h"
#include "potracelib.h"
#include "lists.h"
#include "greymap.h"
#include "render.h"
#include "main.h"
#include "auxiliary.h"
#include "trans.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define UNDEF ((double)(1e30))   /* a value to represent "undefined" */


void calc_dimensions(imginfo_t *imginfo, potrace_path_t *plist, float scale) {
	double dim_def;
	double maxwidth, maxheight, sc;
	// avoid dividing zero
	if (imginfo->pixwidth == 0) {
		imginfo->pixwidth = 1;
	}
	if (imginfo->pixheight == 0) {
		imginfo->pixheight = 1;
	}

	backend_t b;
	b.name = "pgm";      /* name of this backend */
	b.ext = ".pgm";      /* file extension */
	b.fixed = 0;         /* fixed page size backend? */
	b.pixel = 1;         /* pixel-based backend? */
	b.multi = 1;         /* multi-page backend? */
	b.init_f = NULL;     /* initialization function */
	b.page_f = page_pgm; /* per-bitmap function */
	b.term_f = NULL;	 /* finalization function */
	b.opticurve = 1;     /* opticurve capable (true Bezier curves?) */

	if (b.pixel) {
		dim_def = DIM_PT;
	}
	else {
		dim_def = DEFAULT_DIM;
	}

	/* apply default dimension to width, height, margins */
	imginfo->width = UNDEF;
	imginfo->height = UNDEF;
	imginfo->lmar = UNDEF;
	imginfo->rmar = UNDEF;
	imginfo->tmar = UNDEF;
	imginfo->bmar = UNDEF;

	/* start with a standard rectangle */
	trans_from_rect(&imginfo->trans, imginfo->pixwidth, imginfo->pixheight);
	if (imginfo->width == UNDEF && imginfo->height == UNDEF) {
		imginfo->width = (int)(imginfo->trans.bb[0] * scale);
		imginfo->height = (int)(imginfo->trans.bb[1] * scale);
	}

	/* apply scaling */
	trans_scale_to_size(&imginfo->trans, imginfo->width, imginfo->height);

	if (imginfo->lmar == UNDEF) {
		imginfo->lmar = 0;
	}
	if (imginfo->rmar == UNDEF) {
		imginfo->rmar = 0;
	}
	if (imginfo->bmar == UNDEF) {
		imginfo->bmar = 0;
	}
	if (imginfo->tmar == UNDEF) {
		imginfo->tmar = 0;
	}
}


static void pgm_path(potrace_curve_t *curve, trans_t t, render_t *rm) {
  dpoint_t *c, c1[3];
  int i;
  int m = curve->n;
  
  c = curve->c[m-1];
  c1[2] = trans(c[2], t);
  render_moveto(rm, c1[2].x, c1[2].y);
  
  for (i=0; i<m; i++) {
    c = curve->c[i];
    switch (curve->tag[i]) {
    case POTRACE_CORNER:
      c1[1] = trans(c[1], t);
      c1[2] = trans(c[2], t);
      render_lineto(rm, c1[1].x, c1[1].y);
      render_lineto(rm, c1[2].x, c1[2].y);
      break;
    case POTRACE_CURVETO:
      c1[0] = trans(c[0], t);
      c1[1] = trans(c[1], t);
      c1[2] = trans(c[2], t);
      render_curveto(rm, c1[0].x, c1[0].y, c1[1].x, c1[1].y, c1[2].x, c1[2].y);
      break;
    }
  }
}

int page_pgm(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo) {
  potrace_path_t *p;
  greymap_t *gm;
  render_t *rm;
  int w, h;
  trans_t t;
  int mode;
  const char *comment = "created by POTRACE 1.10, written by XIANG CHAO 2020";

  t.bb[0] = imginfo->trans.bb[0]+imginfo->lmar+imginfo->rmar;
  t.bb[1] = imginfo->trans.bb[1]+imginfo->tmar+imginfo->bmar;
  t.orig[0] = imginfo->trans.orig[0]+imginfo->lmar;
  t.orig[1] = imginfo->trans.orig[1]+imginfo->bmar;
  t.x[0] = imginfo->trans.x[0];
  t.x[1] = imginfo->trans.x[1];
  t.y[0] = imginfo->trans.y[0];
  t.y[1] = imginfo->trans.y[1];

  w = (int)ceil(t.bb[0]);
  h = (int)ceil(t.bb[1]);

  gm = gm_new(w, h);
  if (!gm) {
    return 1;
  }
  rm = render_new(gm);
  if (!rm) {
    return 1;
  }

  gm_clear(gm, 255); /* white */

  list_forall(p, plist) {
    pgm_path(&p->curve, t, rm);
  }

  render_close(rm);

  /* if negative orientation, make sure to invert effect of rendering */
  mode = imginfo->width * imginfo->height < 0 ? GM_MODE_NEGATIVE : GM_MODE_POSITIVE;

  gm_writepgm(fout, rm->gm, comment, 1, mode, 2.2);

  render_free(rm);
  gm_free(gm);

  return 0;
}


int render_pgm(cv::Mat & mask, potrace_path_t *plist, imginfo_t *imginfo) {
	potrace_path_t *p;
	greymap_t *gm;
	render_t *rm;
	int w, h;
	trans_t t;
	int mode;
	
	t.bb[0] = imginfo->trans.bb[0] + imginfo->lmar + imginfo->rmar;
	t.bb[1] = imginfo->trans.bb[1] + imginfo->tmar + imginfo->bmar;
	t.orig[0] = imginfo->trans.orig[0] + imginfo->lmar;
	t.orig[1] = imginfo->trans.orig[1] + imginfo->bmar;
	t.x[0] = imginfo->trans.x[0];
	t.x[1] = imginfo->trans.x[1];
	t.y[0] = imginfo->trans.y[0];
	t.y[1] = imginfo->trans.y[1];

	w = (int)ceil(t.bb[0]);
	h = (int)ceil(t.bb[1]);

	gm = gm_new(w, h);
	if (!gm) {
		return 1;
	}
	rm = render_new(gm);
	if (!rm) {
		return 1;
	}

	gm_clear(gm, 255); /* white */

	list_forall(p, plist) {
		pgm_path(&p->curve, t, rm);
	}

	render_close(rm);
	render_free(rm);
	
	// convert from grey map into binary mask in opencv
	gm_to_mat(mask, gm);

	gm_free(gm);

	return 0;
}

